package tcp

import (
	"crypto/rand"
	"crypto/tls"
	"net"
	"strings"
	"time"

	"gddgame.cc/galaxy/satellite"

	"gddgame.cc/galaxy/utils/def"
)

type tcp struct {
	def.Logger

	addr      *net.TCPAddr
	tls       *tls.Config
	listener  net.Listener
	connected bool
	accept    chan satellite.Conn
}

func NewTransport(tcpAddr *net.TCPAddr) *tcp {
	t := &tcp{
		addr: tcpAddr,
	}
	return t
}

func (transport *tcp) Dial() (satellite.Conn, error) {
	conn, err := net.DialTCP("tcp", nil, transport.addr)
	if err != nil {
		return nil, err
	}

	return satellite.NewBaseConn(conn, transport), err
}
func (transport *tcp) Listen() (<-chan satellite.Conn, error) {
	listener, err := net.ListenTCP("tcp", transport.addr)
	if err != nil {
		return nil, err
	}
	if transport.tls != nil {
		transport.listener = tls.NewListener(transport.listener, transport.tls)
	} else {
		transport.listener = listener
	}
	transport.connected = true
	if transport.Logger != nil {
		transport.Debugf("Listen tcp: %#v", transport.addr)
	}
	transport.accept = make(chan satellite.Conn)
	go func() {
		for {
			if !transport.connected {
				goto Close
			}
			conn, err := transport.listener.Accept()
			if err != nil {
				if strings.Contains(err.Error(), "closed") {
					goto Close
				}
				if transport.Logger != nil {
					transport.Errorf("Socket err:%s", err)
				}
			} else {
				if transport.Logger != nil {
					transport.Debugf("tcp new conn:#v", conn)
				}
				transport.accept <- satellite.NewBaseConn(conn, transport)
			}
		}
	Close:
		//fmt.Println("tcp accept close")
		close(transport.accept)
		_ = transport.Close()
	}()
	return transport.accept, nil
}

func (transport *tcp) Tsl(certFile string, keyFile string) error {
	crt, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	tlsConfig := &tls.Config{}
	tlsConfig.Certificates = []tls.Certificate{crt}
	// Time returns the current time as the number of seconds since the epoch.
	// If Time is nil, TLS uses time.Now.
	tlsConfig.Time = time.Now
	// Rand provides the source of entropy for nonces and RSA blinding.
	// If Rand is nil, TLS uses the cryptographic random reader in package
	// crypto/rand.
	// The Reader must be safe for use by multiple goroutines.
	tlsConfig.Rand = rand.Reader
	transport.tls = tlsConfig
	return nil
}
func (transport *tcp) Type() satellite.TransportType {
	return "tcp"
}
func (transport *tcp) Schema() string {
	return transport.addr.String()
}
func (transport *tcp) SetLogger(logger def.Logger) {
	transport.Logger = logger
}

func (transport *tcp) Close() error {
	if !transport.connected {
		return nil
	}
	//fmt.Println("close tcp")
	transport.connected = false
	err := transport.listener.Close()
	if err != nil {
		return err
	}
	return nil
}

func (transport *tcp) Connected() bool {
	return transport.connected
}
